using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Drawing.Text;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Web;
using System.IO;
using Possan.Graphics.Filters;

namespace Possan.Graphics
{
	public class Utilities
	{
		/// <summary>
		/// Skins an bitmap onto another bitmap, stretching only the top/bottom/right/left and center-parts, keeping the corners untouched, often referred to as GRID9 or 9-GRID skinning, please ensure that both bitmaps are of the same DPI 
		/// </summary>
		/// <param name="target">Target/output bitmap</param>
		/// <param name="skin">Skin bitmap</param>
		/// <param name="leftmargin">Margin size, Left</param>
		/// <param name="topmargin">Margin size, Top</param>
		/// <param name="rightmargin">Margin size, Right</param>
		/// <param name="bottommargin">Margin size, Bottom</param>
		/// <param name="outputleft">Output origin, Left</param>
		/// <param name="outputtop">Output origin, Top</param>
		/// <param name="outputwidth">Output size, Width</param>
		/// <param name="outputheight">Output size, Height</param>
		public static void Skin9Grid(
			Bitmap target, Bitmap skin,
			int leftmargin, int topmargin, int rightmargin, int bottommargin,
			int outputleft, int outputtop, int outputwidth, int outputheight)
		{

			System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(target);


			int u0 = 0;
			int u1 = leftmargin;
			int u2 = skin.Width - rightmargin;
			int u3 = skin.Width;

			int v0 = 0;
			int v1 = topmargin;
			int v2 = skin.Height - bottommargin;
			int v3 = skin.Height;

			int x0 = outputleft;
			int x1 = outputleft + leftmargin;
			int x2 = outputleft + outputwidth - rightmargin;
			int x3 = outputleft + outputwidth;

			int y0 = outputtop;
			int y1 = outputtop + topmargin;
			int y2 = outputtop + outputheight - bottommargin;
			int y3 = outputtop + outputheight;

			// rita långsidorna

			//	g.ResetTransform();
			g.CompositingQuality = CompositingQuality.HighQuality;
			g.InterpolationMode = InterpolationMode.HighQualityBicubic;
			g.PixelOffsetMode = PixelOffsetMode.HighQuality;

			g.InterpolationMode = InterpolationMode.Bilinear;

			g.DrawImage(skin, Rectangle.FromLTRB(x1, y0, x2, y1), Rectangle.FromLTRB(u1, v0, u2, v1), GraphicsUnit.Pixel); // uppe
			g.DrawImage(skin, Rectangle.FromLTRB(x1, y2, x2, y3), Rectangle.FromLTRB(u1, v2, u2, v3), GraphicsUnit.Pixel); // nere
			g.DrawImage(skin, Rectangle.FromLTRB(x0, y1, x1, y2), Rectangle.FromLTRB(u0, v1, u1, v2), GraphicsUnit.Pixel); // vänster
			g.DrawImage(skin, Rectangle.FromLTRB(x2, y1, x3, y2), Rectangle.FromLTRB(u2, v1, u3, v2), GraphicsUnit.Pixel); // höger

			// rita mitten

			g.DrawImage(skin, Rectangle.FromLTRB(x1, y1, x2, y2), Rectangle.FromLTRB(u1, v1, u2, v2), GraphicsUnit.Pixel); // mitten

			// rita hörnen

			g.DrawImage(skin, x0, y0, Rectangle.FromLTRB(u0, v0, u1, v1), GraphicsUnit.Pixel); // uppe vänster
			g.DrawImage(skin, x2, y0, Rectangle.FromLTRB(u2, v0, u3, v1), GraphicsUnit.Pixel); // uppe höger
			g.DrawImage(skin, x0, y2, Rectangle.FromLTRB(u0, v2, u1, v3), GraphicsUnit.Pixel); // nere vänster
			g.DrawImage(skin, x2, y2, Rectangle.FromLTRB(u2, v2, u3, v3), GraphicsUnit.Pixel); // nere höger
		}






		/// <summary>
		/// Check if bitmap is only background in specific area, for cropping etc.
		/// </summary>
		/// <param name="img"></param>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <param name="w"></param>
		/// <param name="h"></param>
		/// <param name="bg"></param>
		/// <returns></returns>
		public static bool IsBackgroundOnly(Bitmap img, int x, int y, int w, int h, Color bg)
		{
			for (int j = 0; j < h ;  j++)
			{
				for (int i = 0; i < w ;  i++)
				{
					try
					{
						Color col = img.GetPixel(x + i, y + j);
						if (Math.Abs(col.R - bg.R) > 5 ||
							Math.Abs(col.G - bg.G) > 5 ||
							Math.Abs(col.B - bg.B) > 5 ||
							Math.Abs(col.A - bg.A) > 10)
							return false;
					}
					catch (Exception)
					{    
					}
				}
			}

			     return true;
		}


		/// <summary>
		/// Render a text string into a transparent bitmap, using the specific font
		/// </summary>
		/// <param name="fontsizepixels"></param>
		/// <param name="text"></param>
		/// <param name="color"></param>
		/// <param name="fontfile"></param>
		/// <param name="fontname"></param>
		/// <returns></returns>
		public static Bitmap RenderString(float fontsizepixels, string text, Color color, byte[] fontdata, string fontname, FontStyle fontstyle)
		{
			//Logger.Info(typeof(GraphicsUtilities), "RenderString( fontsizepixels=" + fontsizepixels + ", text=" + text + ", color=" + color + ", fontdata=" + fontdata + ", fontname=" + fontname + ", fontstyle=" + fontstyle + " )");
			     return RenderString(fontsizepixels, text, color, fontdata, fontname, fontstyle, 8);
		}

		/// <summary>
		/// Render a text string into a transparent bitmap, using the specific font
		/// </summary>
		/// <param name="fontsizepixels"></param>
		/// <param name="text"></param>
		/// <param name="color"></param>
		/// <param name="fontfile"></param>
		/// <param name="fontname"></param>
		/// <returns></returns>
		public static Bitmap RenderString(float fontsizepixels, string text, Color color, byte[] fontdata, string fontname, FontStyle fontstyle, int upsampling)
		{
			//Logger.Info(typeof(GraphicsUtilities), "RenderString( fontsizepixels=" + fontsizepixels + ", text=" + text + ", color=" + color + ", fontdata=" + fontdata + ", fontname=" + fontname + ", fontstyle=" + fontstyle + ", upsampling=" + upsampling + " )");

			try
			{
				Font f = null;
				float fontsize = fontsizepixels * upsampling;
				if (fontdata != null )
				{
					PrivateFontCollection m_FontCollection = new PrivateFontCollection();

					IntPtr memory = Marshal.AllocHGlobal(fontdata.Length);
					Marshal.Copy(fontdata, 0, memory, fontdata.Length);
					m_FontCollection.AddMemoryFont(memory, fontdata.Length);

					try
					{
						// f = new Font(new FontFamily("HelveticaNeue LT 57 Cn", m_FontCollection), fontsize, FontStyle.Regular, GraphicsUnit.Pixel);//, o.m_FontCollection );
						f = new Font(new FontFamily(fontname, m_FontCollection), fontsize, fontstyle, GraphicsUnit.Pixel);//, o.m_FontCollection );
						// f = new Font( new FontFamily( "Helonia Neue" ), size, FontStyle.Bold, GraphicsUnit.Pixel );//, o.m_FontCollection );
					}
					catch (Exception x)
					{
						// Logger.Error(typeof(GraphicsUtilities), x);
					}

					Marshal.FreeHGlobal(memory);
				}

				if (f != null)
				{
					     return RenderString(text, color, f, upsampling);

				}
			}
			catch (Exception x2)
			{
				// Logger.Error(typeof(GraphicsUtilities), x2);
			}

			     return null;
		}


		/// <summary>
		/// Render a text string into a transparent bitmap, using the specific font
		/// </summary>
		/// <param name="fontsizepixels"></param>
		/// <param name="text"></param>
		/// <param name="color"></param>
		/// <param name="fontfile"></param>
		/// <param name="fontname"></param>
		/// <returns></returns>
		public static Bitmap RenderString(float fontsizepixels, string text, Color color, string fontfile, string fontname, FontStyle fontstyle)
		{
			// Logger.Info(typeof(GraphicsUtilities), "RenderString( fontsizepixels=" + fontsizepixels + ", text=" + text + ", color=" + color + ", fontfile=" + fontfile + ", fontname=" + fontname + ", fontstyle=" + fontstyle + " )");

			     return RenderString(fontsizepixels, text, color, fontfile, fontname, fontstyle, 8);
		}

		/// <summary>
		/// Render a text string into a transparent bitmap, using the specific font
		/// </summary>
		/// <param name="fontsizepixels"></param>
		/// <param name="text"></param>
		/// <param name="color"></param>
		/// <param name="fontfile"></param>
		/// <param name="fontname"></param>
		/// <returns></returns>
		public static Bitmap RenderString(float fontsizepixels, string text, Color color, string fontfile, string fontname, FontStyle fontstyle, int upsampling)
		{
			//	Logger.Info(typeof(GraphicsUtilities), "RenderString( fontsizepixels=" + fontsizepixels + ", text=" + text + ", color=" + color + ", fontfile=" + fontfile + ", fontname=" + fontname + ", fontstyle=" + fontstyle + ", upsampling=" + upsampling + " )");

			try
			{
				Font f = null;

				float fontsize = fontsizepixels * upsampling;

				if (!String.IsNullOrEmpty(fontfile))
				{
					PrivateFontCollection m_FontCollection = new PrivateFontCollection();
					m_FontCollection.AddFontFile(fontfile);

					try
					{
						// f = new Font(new FontFamily("HelveticaNeue LT 57 Cn", m_FontCollection), fontsize, FontStyle.Regular, GraphicsUnit.Pixel);//, o.m_FontCollection );
						f = new Font(new FontFamily(fontname, m_FontCollection), fontsize, fontstyle, GraphicsUnit.Pixel);//, o.m_FontCollection );
						// f = new Font( new FontFamily( "Helonia Neue" ), size, FontStyle.Bold, GraphicsUnit.Pixel );//, o.m_FontCollection );
					}
					catch (Exception x)
					{
						//	Logger.Error(typeof(GraphicsUtilities), x);
					}
				}
				else
				{
					try
					{

						f = new Font(fontname, fontsize);
					}
					catch (Exception x)
					{
						// Logger.Error(typeof(GraphicsUtilities), x);
					}
				}

				if (f != null)
				{
					     return RenderString(text, color, f, upsampling);
				}
			}
			catch (Exception x2)
			{
				// Logger.Error(typeof(GraphicsUtilities), x2);
			}

			     return null;
		}


		/// <summary>
		/// Render a text string into a transparent bitmap, using the specific font
		/// </summary>
		/// <param name="fontsizepixels"></param>
		/// <param name="text"></param>
		/// <param name="color"></param>
		/// <param name="font"></param>
		/// <returns></returns>
		public static Bitmap RenderString(string text, Color color, Font font, int upsampling)
		{
			     return RenderString(text, color, font, upsampling, -1, StringAlignment.Near);
		}

		public static Bitmap RenderString(string text, Color color, Font font, int upsampling, int maxwidth)
		{
			     return RenderString(text, color, font, upsampling, maxwidth, StringAlignment.Near);
		}

		public static Bitmap RenderString(string text, Color color, Font font, int upsampling, int maxwidth, StringAlignment alignment)
		{
			try
			{
				Font f = font;

				if (f != null)
				{
					Font f2 = f;// new Font(f.ge, f.Size * upsampling);
					// f.Size *= upsampling;

					System.Drawing.Graphics g;

					Bitmap tempbitmap = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
					tempbitmap.SetResolution(96, 96);
					g = System.Drawing.Graphics.FromImage(tempbitmap);
					g.InterpolationMode = InterpolationMode.HighQualityBicubic;
					g.TextRenderingHint = TextRenderingHint.AntiAlias;//.AntiAliasGridFit;
					StringFormat sf = StringFormat.GenericDefault;
					sf.Alignment = alignment;
					SizeF textsize;
					if (maxwidth > -1)
						textsize = g.MeasureString(text, f2, maxwidth * upsampling, sf);
					else
						textsize = g.MeasureString(text, f2);

					// Response.Write("textsize: " + textsize.Width + " x " + textsize.Height + "<br>");

					int w = (int)textsize.Width + 10;
					int h = (int)(textsize.Height * 0.95);

					if (w > 0 && h > 0)
					{


						Bitmap img = new Bitmap(w, h, PixelFormat.Format32bppArgb);
						img.SetResolution(96, 96);
						g = System.Drawing.Graphics.FromImage(img);
						g.TextRenderingHint = TextRenderingHint.SingleBitPerPixel;//.AntiAliasGridFit;
						g.InterpolationMode = InterpolationMode.NearestNeighbor;//.HighQualityBicubic;
						g.SmoothingMode = SmoothingMode.None;

						Color black = Color.FromArgb(255, 0, 0, 0);
						Color white = Color.FromArgb(255, 255, 255, 255);
						SolidBrush br = new SolidBrush(black);
						SolidBrush br2 = new SolidBrush(white);
						g.FillRectangle(br, 0, 0, w, h);
						g.DrawString(text, f2, br2, new RectangleF(5, 0, w + 10, h + 10), sf);

						Bitmap imgdownscaled = new Bitmap((w / upsampling) + 2, (h / upsampling) + 2, PixelFormat.Format32bppArgb);
						imgdownscaled.SetResolution(96, 96);
						g = System.Drawing.Graphics.FromImage(imgdownscaled);
						g.FillRectangle(br, 0, 0, w + 2, h + 2);
						g.InterpolationMode = InterpolationMode.HighQualityBicubic;
						g.SmoothingMode = SmoothingMode.None;
						g.DrawImage(img, new Rectangle(0, 0, w / upsampling, h / upsampling), 1, 0, w, h, GraphicsUnit.Pixel);

						w /= upsampling;//(int)imgdownscaled.Width;
						h /= upsampling;//(int)imgdownscaled.Height;
						w += 2;
						h += 2;

						Trimmer tr = new Trimmer();
						tr.Left = true;
						tr.Top = false;
						tr.Right = true;
						tr.Bottom = false;
						Bitmap img2 = tr.Filter(imgdownscaled);

						for (int y = 0; y < img2.Height; y++)
						{
							for (int x = 0; x < img2.Width; x++)
							{
								Color c = img2.GetPixel(x, y);
								Color c2 = Color.FromArgb(c.R, color);
								img2.SetPixel(x, y, c2);
							}
						}

						     return img2;
					}
					else
					{
						//				Logger.Warning(typeof(GraphicsUtilities), "Output image too small.");
					}
				}
				else
				{
					//			Logger.Warning(typeof(GraphicsUtilities), "No font loaded.");
				}
			}
			catch (Exception x2)
			{
				//		Logger.Error(typeof(GraphicsUtilities), x2);
			}

			     return null;
		}







		/// <summary>
		/// Try to parse numeric (integer) value, returns 0 if parsing fails.
		/// </summary>
		/// <param name="obj">Object to parse</param>
		/// <returns>Parsed value</returns>
		public static int ParseInt(object obj)
		{
			     return ParseInt(obj, 0);
		}


		/// <summary>
		/// Try to parse numeric (integer) value, returns a default value if parsing fails.
		/// </summary>
		/// <param name="obj">Object to parse</param>
		/// <param name="defaultvalue">Default value if parser fails</param>
		/// <returns>Parsed value</returns>
		public static int ParseInt(object obj, int defaultvalue)
		{
			if (obj == null)
				return defaultvalue;

			try
			{
				int i = int.Parse(obj.ToString());
				     return i;
			}
			catch (Exception)
			{
			}

			     return defaultvalue;
		}



		/// <summary>
		/// Parse an HTML-Color code into a System.Drawing.Color object, reads these formats:
		/// RRGGBB
		/// RRGGBBAA
		/// RGB
		/// RGBA
		/// #RRGGBB
		/// #RRGGBBAA
		/// #RGB
		/// #RGBA
		/// rrr,ggg,bbb
		/// rrr,ggg,bbb,aaa
		/// </summary>
		/// <param name="input">color code</param>
		/// <returns>Color object, white if parser fails</returns>
		public static Color ParseHtmlColor(string input)
		{
			     return ParseHtmlColor(input, Color.FromArgb(255, 255, 255, 255));
		}

		/// <summary>
		/// Parse an HTML-Color code into a System.Drawing.Color object, reads these formats:
		/// RRGGBB
		/// RRGGBBAA
		/// RGB
		/// RGBA
		/// #RRGGBB
		/// #RRGGBBAA
		/// #RGB
		/// #RGBA
		/// rrr,ggg,bbb
		/// rrr,ggg,bbb,aaa
		/// </summary>
		/// <param name="input">color code</param>
		/// <param name="defaultvalue">default value if parser error</param>
		/// <returns>Color object, or default value</returns>
		public static Color ParseHtmlColor(string input, Color defaultvalue)
		{
			input = input.Trim();
			if (input.StartsWith("#"))
				input = input.Substring(1);

			bool err = true;
			int red = 255;
			int gre = 255;
			int blu = 255;
			int alp = 255;

			if (input.Contains(","))
			{
				string[] tmp = input.Split(',');

				if (tmp.Length >= 3 && tmp.Length <= 4)
				{
					// r,g,b
					red = Utilities.ParseInt(tmp[0], -1);
					gre = Utilities.ParseInt(tmp[1], -1);
					blu = Utilities.ParseInt(tmp[2], -1);

					if (tmp.Length == 4)
					{
						// r,g,b,a
						alp = Utilities.ParseInt(tmp[3], -1);
					}

					err = (red < 0 || gre < 0 || blu < 0 || alp < 0);
				}
			}
			else
			{

				if (input.Length == 3)
				{
					// #RGB
					red = Convert.ToInt32(input.Substring(0, 1), 16) * 17;
					gre = Convert.ToInt32(input.Substring(1, 1), 16) * 17;
					blu = Convert.ToInt32(input.Substring(2, 1), 16) * 17;
					err = false;
				}
				else if (input.Length == 4)
				{
					// #RGBA
					red = Convert.ToInt32(input.Substring(0, 1), 16) * 17;
					gre = Convert.ToInt32(input.Substring(1, 1), 16) * 17;
					blu = Convert.ToInt32(input.Substring(2, 1), 16) * 17;
					alp = Convert.ToInt32(input.Substring(3, 1), 16) * 17;
					err = false;
				}
				else if (input.Length == 6)
				{
					// #RRGGBB
					red = Convert.ToInt32(input.Substring(0, 2), 16);
					gre = Convert.ToInt32(input.Substring(2, 2), 16);
					blu = Convert.ToInt32(input.Substring(4, 2), 16);
					err = false;
				}
				else if (input.Length == 8)
				{
					// #RRGGBBAA	 
					red = Convert.ToInt32(input.Substring(0, 2), 16);
					gre = Convert.ToInt32(input.Substring(2, 2), 16);
					blu = Convert.ToInt32(input.Substring(4, 2), 16);
					alp = Convert.ToInt32(input.Substring(6, 2), 16);
					err = false;
				}
			}

			if (err)
				return defaultvalue;

			     return Color.FromArgb(alp, red, gre, blu);
		}

		public static Bitmap NewBitmap(int width, int height, int dpi)
		{
			Bitmap outputbitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
			outputbitmap.SetResolution(dpi, dpi);
			     return outputbitmap;
		}

		public static Bitmap NewBitmap(int width, int height)
		{
			     return NewBitmap(width, height, 72);
		}

		public static Bitmap CloneBitmap(Bitmap input, int dpi)
		{
			if (input != null)
			{
				Bitmap outputbitmap = new Bitmap(input.Width, input.Height, PixelFormat.Format32bppArgb);
				outputbitmap.SetResolution(input.HorizontalResolution, input.VerticalResolution);

				System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(outputbitmap);
				g.DrawImageUnscaled(input, 0, 0);

				outputbitmap.SetResolution(dpi, dpi);

				     return outputbitmap;
			}
			     return null;
		}

		public static Bitmap CloneBitmap(Bitmap input)
		{
			     return CloneBitmap(input, 72);
		}

	}
}





